package ms.utils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;

public class NamedList<E> extends ArrayList<E> {

	private static final long serialVersionUID = 1L;

	private Method getNameMethod = null;
	private Field nameField = null;
	private boolean firstTime = true;

	private EventListImpl<ItemChangedArg<E>> itemChangedListener;

	public static class ItemChangedArg<E> {
		public final E item;
		public final Type type;

		public enum Type {
			ADDED, REMOVED, RESET
		}

		public ItemChangedArg(Type type, E item) {
			this.item = item;
			this.type = type;
		}
	}

	public synchronized void addItemChangedListener(Event<ItemChangedArg<E>> listener) {
		if (itemChangedListener == null)
			itemChangedListener = new EventListImpl<ItemChangedArg<E>>();
		itemChangedListener.add(listener);
	}

	public synchronized void removeItemChangedListener(Event<ItemChangedArg<E>> listener) {
		if (itemChangedListener != null)
			itemChangedListener.remove(listener);
	}

	protected void fireItemChanged(ItemChangedArg.Type type, E item) {
		if (itemChangedListener != null)
			itemChangedListener.invoke(this, new ItemChangedArg<E>(type, item));
	}

	protected void determineGetNameMethod(E item) throws Exception {
		if (!firstTime)
			return;
		firstTime = true;

		if ((getNameMethod = item.getClass().getMethod("getName")) == null)
			if ((getNameMethod = item.getClass().getMethod("Name")) == null)
				if ((nameField = item.getClass().getField("Name")) == null)
					throw new Exception("Unable to get item's name");
	}

	@Override
	public boolean add(E e) {
		boolean result = super.add(e);
		fireItemChanged(ItemChangedArg.Type.ADDED, e);
		return result;
	};

	@SuppressWarnings("unchecked")
	@Override
	public boolean remove(Object o) {
		boolean result = super.remove(o);
		if (result)
			fireItemChanged(ItemChangedArg.Type.REMOVED, (E) o);
		return result;
	}

	protected String getName(E item) throws Exception {
		determineGetNameMethod(item);

		return (String) (getNameMethod != null ? getNameMethod.invoke(item) : nameField.get(item));
	}

	public int indexOf(String name) throws Exception {
		for (int i = 0; i < size(); i++)
			if (name.equalsIgnoreCase(getName(get(i))))
				return i;
		return -1;
	}

	public E get(String name) {
		int idx = -1;
		try {
			idx = indexOf(name);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return idx != -1 ? super.get(idx) : null;
	}

}
